// file: system/task/wait.c
// autor: jiangxinpeng
// time: 2022.1.25
// copyright: (C) 2020-2050 by jiangxinpeng,All right are reserved.

#include <os/task.h>
#include <os/process.h>
#include <os/pthread.h>
#include <os/safety.h>
#include <lib/errno.h>
#include <lib/list.h>
#include <sys/wait.h>
#include <os/schedule.h>

static int WaitAnyHanggingChild(task_t *parent, int *status)
{
    task_t *child, *next;

    list_traversal_all_owner_to_next_safe(child, next, &task_global_list, global_list)
    {
        if (child->parent_pid == parent->pid)
        {
            if (child->status == TASK_HANGING)
            {
                if (!status)
                {
                    *status = child->exit_status;
                }
                if (TASK_IS_SINGAL_THREAD(child))
                {
                    ProcessDestroy(child, 0);
                }
                else
                {
                    ProcessDestroy(child, 1);
                }
                return child->pid;
            }
        }
    }
    return -1;
}

static int WaitOneHanggingChild(task_t *parent, int pid, int *status)
{
    task_t *child, *next;

    list_traversal_all_owner_to_next_safe(child, next, &task_global_list, global_list)
    {
        if (child->parent_pid == pid)
        {
            if (child->status == TASK_HANGING)
            {
                if (!status)
                {
                    *status = child->status;
                }
                if (TASK_IS_SINGAL_THREAD(child))
                {
                    ProcessDestroy(child, 0);
                }
                else
                {
                    ProcessDestroy(child, 1);
                }
                return pid;
            }
        }
    }
    return -1;
}

pid_t SysWaitPid(pid_t pid, int *status, int op)
{
    task_t *parent = cur_task;
    pid_t child_pid;
    int wait_status;

    if ((status && SafetyCheckRange(status, sizeof(int))))
    {
        KPrint("[waitpid] status mem err\n");
        return -1;
    }

    while (1)
    {
        //TASK_CHECK_THREAD_CANNELATION(parent);
        if (pid = -1)
        {
            // waitting any children process
            if ((child_pid = WaitAnyHanggingChild(parent, &wait_status)) > 0)
            {
                if (status)
                {
                    MemCopyToUser(status, &wait_status, sizeof(int));
                }
                return child_pid;
            }
        }
        else
        {
            // waitting assign children process
            if ((child_pid = WaitOneHanggingChild(parent, pid, status)) > 0)
            {
                if (status)
                {
                    MemCopyToUser(status, &wait_status, sizeof(int));
                }
                return child_pid;
            }
        }

        // check the zombie children process
        if ((child_pid = ProcessDealZombieChild(parent)) > 0)
        {
            if (pid == -1 || child_pid == pid)
            {
                return child_pid;
            }
        }
        if (!TaskCountChild(parent))
        {
            //KPrint("[waitpid] parent task no child\n");
            return -1;
        }
        // task if wait child task exit
        if (op & WNOHANG)
        {
            //KPrint("[waitpid] no hang flags set\n");
            return 0;
        }
        KPrint("[waitpid] task block wait child exit\n");
        TaskBlock(TASK_WAITTING);
        if (ExceptionCauseExitWhenWait(&parent->exception_manager))
        {
            KPrint("[waitpid] child exit because error\n");
            return -EINTR;
        }
        KPrint("{waitpid] pid %d exit ok\n", pid);
    }
    return -1;
}
